﻿using Devil;
using GameToolkit.ContentProvider;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditorInternal.VR;
using UnityEngine;

namespace GameToolkit.Editor
{
    using Data = TablesForEditor.Data;

    public interface IReferencedAssetEditor
    {
        Object referencedTarget { get; }
    }

    [CustomPropertyDrawer(typeof(TableReferenceAttribute))]
    [CustomPropertyDrawer(typeof(TableReference))]
    public class TableReferenceDrawer : PropertyDrawer
    {
        static GUIContent sDropBtn = new GUIContent("▼");
        static GUIContent sFileBtn = new GUIContent("File");
        static Dictionary<int, DropdownMenu> sMenuCache = new Dictionary<int, DropdownMenu>();

#pragma warning disable IDE0051
        [InitializeOnLoadMethodParallel(IsOnMainThread = true)]
        static void ClearMenuCache()
        {
            sMenuCache.Clear();
        }
#pragma warning restore

        public static DropdownMenu GetDropdownMenu(TableSet<Data> table, string spaceName, string tabProperty, int pageSize)
        {
            var id = MathExt.Hash(table.TableName.IgnoreCaseToHash(), tabProperty == null ? 0 : tabProperty.ToHash(), pageSize);
            DropdownMenu menu;
            if (!sMenuCache.TryGetValue(id, out menu))
            {
                menu = new DropdownMenu(table, spaceName, tabProperty, pageSize);
                sMenuCache[id] = menu;
            }
            return menu;
        }

        //public static DropdownMenu GetDropdownMenu(TableSet<Data> table, string property, int pageSize)
        //{
        //    var id = MathExt.Hash(table.TableName.IgnoreCaseToHash(), property.ToHash(), pageSize);
        //    DropdownMenu menu;
        //    if (!sMenuCache.TryGetValue(id, out menu))
        //    {
        //        menu = new DropdownMenu(table, property, pageSize);
        //        sMenuCache[id] = menu;
        //    }
        //    return menu;
        //}

        public class DropdownMenu
        {
            GenericMenu menu;
            SerializedProperty property;

            public DropdownMenu(TableSet<Data> table, string spaceName, string propertyName, int pageSize)
            {
                menu = new GenericMenu();
                GenericMenu.MenuFunction2 action = (dt) =>
                {
                    if (property != null)
                    {
                        var id = dt is Data mdt ? mdt.DataId : 0;
                        //var dirty = id != property.intValue;
                        property.intValue = id;
                        property.serializedObject.ApplyModifiedProperties();
                    }
                };
                var arr = table.AsArray();
                menu.AddItem(new GUIContent("Nothing"), false, action, null);
                string prefix = null;
                bool page = pageSize >= 5 && arr.Length > pageSize;
                for (int i = 0; i < arr.Length; i++)
                {
                    var txt = arr[i].GetPropertyTitle(propertyName, true);
                    if (page)
                    {
                        if (i % pageSize == 0)
                        {
                            var max = Mathf.Min(arr.Length - 1, i + 19);
                            var txt1 = arr[i].GetPropertyTitle(propertyName, true);
                            txt1 = StringUtil.Clamp(txt1, 10);
                            var txt2 = arr[max].GetPropertyTitle(propertyName, true);
                            txt2 = StringUtil.Clamp(txt2, 10);
                            prefix = $"\"{txt1}\" ~ \"{txt2}\"/";
                        }
                        txt = ParallelUtils.Concat(prefix, txt);
                    }
                    menu.AddItem(new GUIContent($"{spaceName}/{txt}"), false, action, arr[i]);
                }
            }

            public void Dropdown(Rect position, SerializedProperty property)
            {
                this.property = property.Copy();
                menu.DropDown(position);
            }

            public void ShowAsContext(SerializedProperty property)
            {
                this.property = property;
                menu.ShowAsContext();
            }
        }

        static string FormatFilePath(SerializedProperty property, string file, out string spaceName)
        {
            if (string.IsNullOrEmpty(file))
            {
                spaceName = null;
                return null;
            }
            var regex = new Regex("/%%[a-z 0-9_]+/", RegexOptions.IgnoreCase);
            var match = regex.Match(file);
            if (match != null && match.Success)
            {
                var prefix = file.Substring(0, match.Index + 1);
                var subfix = file.Substring(match.Index + match.Length - 1);
                var reftarget = property.serializedObject.targetObject;
                if(reftarget is IReferencedAssetEditor refasset)
                {
                    var inst = refasset.referencedTarget;
                    if (inst != null)
                        reftarget = inst;
                }
                var asset = AssetDatabase.GetAssetPath(reftarget);
                if (!string.IsNullOrEmpty(asset))
                {
                    var folder = NativeFileSystem.GetFolderName(asset);
                    while (!string.IsNullOrEmpty(folder))
                    {
                        var n = folder.LastIndexOf('/');
                        var pname = n > 0 ? folder.Substring(n + 1) : folder;
                        folder = n > 0 ? folder.Substring(0, n) : null;
                        var path = ParallelUtils.Concat(prefix, pname, subfix);
                        if (NativeFileSystem.HasFile(path))
                        {
                            spaceName = pname;
                            return path;
                        }
                    }
                }
                var defname = match.Value.Substring(3, match.Length - 4);
                spaceName = defname;
                return ParallelUtils.Concat(prefix, defname, subfix);
            }
            spaceName = NativeFileSystem.GetFolderName(file);
            var cur = spaceName.LastIndexOf('/');
            if (cur > 0)
                spaceName = spaceName.Substring(cur + 1);
            return file;
        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUI.BeginProperty(position, label, property);
            position = EditorGUI.PrefixLabel(position, label);
            var lv = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;
            if (property.hasMultipleDifferentValues)
            {
                EditorGUI.LabelField(position, "'is editing multi values.'");
            }
            else
            {
                SerializedProperty idProperty = property;
                var att = attribute as TableReferenceAttribute;
                string filePath = att == null? null: att.TableFile;
                if(property.propertyType != SerializedPropertyType.Integer)
                {
                    idProperty = property.FindPropertyRelative("m_Id");
                    var fileProperty = property.FindPropertyRelative("m_File");
                    var path = string.IsNullOrEmpty(fileProperty.stringValue) ? null : AssetDatabase.GUIDToAssetPath(fileProperty.stringValue);

                    var rect = new Rect(position.x, position.y, Mathf.Min(90, position.width * 0.35f), position.height);
                    position.x += rect.width;
                    position.width -= rect.width;

                    var asset = string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAssetAtPath<TextAsset>(path);
                    var asset1 = EditorGUI.ObjectField(rect, asset, typeof(TextAsset), false) as TextAsset;
                    if (asset != asset1)
                    {
                        path = asset1 == null ? null : AssetDatabase.GetAssetPath(asset1);
                        fileProperty.stringValue = path == null ? "" : AssetDatabase.AssetPathToGUID(path);
                        GUI.changed = true;
                    }
                    if (!string.IsNullOrEmpty(path))
                    {
                        var i = string.IsNullOrEmpty(filePath) ? -1 : filePath.LastIndexOf('@');
                        if (i >= 0)
                            filePath = ParallelUtils.Concat(path, filePath.Substring(i));
                        else
                            filePath = path;
                    }
                    //var sel = EditorUtility.SaveFilePanelInProject("Select Table Reference", "")
                }
                string spaceName;
                var file = FormatFilePath(idProperty, filePath, out spaceName);
                var table = string.IsNullOrEmpty(file) ? null : TablesForEditor.RequestTable(file);
                var data = table == null ? null : table[idProperty.intValue];
                sDropBtn.text = data == null ? $"Invalid Data [{idProperty.intValue}]" : data.GetPropertyTitle(att, true);
                EditorGUI.BeginDisabledGroup(table == null);
                var drop = EditorGUI.DropdownButton(position, sDropBtn, FocusType.Keyboard);
                EditorGUI.EndDisabledGroup();
                if (drop)
                {
                    var pageSize = att == null ? 20 : att.PageSize;
                    var tabProperty = att == null ? null : att.TableProperty;
                    if (string.IsNullOrEmpty(tabProperty))
                        tabProperty = "Id";
                    var menu = GetDropdownMenu(table, spaceName, tabProperty, pageSize);
                    menu.Dropdown(position, idProperty);
                }
            }
            EditorGUI.indentLevel = lv;
            EditorGUI.EndProperty();
        }
    }
}
